#include <QtXml>
#include "DataModel.h"
#include "XmlHelper.h"
#include "RequestDescriptor.h"
#include "SrvConnectionLocal.h"
#include "scImpl.h"

#define FCT "SrvConnectionLocal::SrvConnectionLocal"
SrvConnectionLocal::SrvConnectionLocal(QString dbFile) :
	SrvConnection(), db(dbFile), testResult(false)
{
}

#undef FCT
#define FCT "SrvConnectionLocal::~SrvConnectionLocal"
SrvConnectionLocal::~SrvConnectionLocal()
{
	ctrc(Trc::SETUP,APP,FCT)<<endl;
	emit connectionShutdownSig(this);
	for(Requests::iterator it(requs.begin()); it!=requs.end(); ++it)
		delete it.value();
}

#undef FCT
#define FCT "SrvConnectionLocal::errorString"
QString SrvConnectionLocal::errorString() const
{
	return localError+" "+db.errorString();
}

#undef FCT
#define FCT "SrvConnectionLocal::startTest"
unsigned long SrvConnectionLocal::startTest()
{
	unsigned long res(createTrans(0, 0, false));
	emit testReadySig(testResult, res);
	emit connectionAvailableSig(testResult);
	return res;
}

#undef FCT
#define FCT "SrvConnectionLocal::startRequest"
unsigned long SrvConnectionLocal::startRequest(RequestDescriptor * requDesc, unsigned long transactionId, bool bootTransaction)
{
	return createTrans(requDesc, transactionId, bootTransaction);
}

#undef FCT
#define FCT "SrvConnectionLocal::getData"
QBuffer * SrvConnectionLocal::getData(unsigned long transId)
{
	Requests::iterator it(requs.find(transId));
	Q_ASSERT(it!=requs.end());
	return it.value();
}

#undef FCT
#define FCT "SrvConnectionLocal::freeData"
void SrvConnectionLocal::freeData(unsigned long transId)
{
	if(transId==0xffffffff)
		return;
	Requests::iterator it(requs.find(transId));
	if(it!=requs.end()) // ignore double free's
	{
		emit transactionDeleteSig(transId);
		delete it.value();
		requs.erase(it);
	}
}

#undef FCT
#define FCT "SrvConnectionLocal::createTrans"
unsigned long SrvConnectionLocal::createTrans(RequestDescriptor * requDesc, unsigned long transactionId, bool bootTransaction)
{
	unsigned long res(transactionId?transactionId:createTransactionId());
	QBuffer * dataBuf(new QBuffer);
	dataBuf->open(QIODevice::WriteOnly);
	requs.insert(res, dataBuf);
	QXmlStreamWriter * wr(0); wr=XmlHelper::createXmlDoc(dataBuf);
	if(bootTransaction)
	{
		if(!requDesc)
			throw new AppException(FCT ": boot request must not be test", true);
		wr->writeStartElement(XmlHelper::domain2DomainText(DataModel::get()->entityId2Domain(requDesc->begin()->second.entity)));
		for(RequestDescriptor::iterator it(requDesc->begin()); it!=requDesc->end(); ++it)
			db.selectBoot(it->second, wr);
		wr->writeEndElement(); // domain name
		XmlHelper::finishXmlDoc(wr);
		dataBuf->close();
		emit dataReadySig(res);
		return 0; // we serve the request always synchronously
	}
	if(requDesc)
	{
		RequestDescriptor *depRequs(new RequestDescriptor), *currentRequ(requDesc);
		DataModel *dm(DataModel::get());
		int followOrder(0);
		while(currentRequ->size())
		{
			ctrc(Trc::WORK,APP,FCT)<<*currentRequ<<endl;
			DataDomain currentDomain(DOMAIN_UNDEF);
			for(RequestDescriptor::iterator it(currentRequ->begin()); it!=currentRequ->end(); ++it)
			{
				DataDomain newDomain(dm->entityId2Domain(it->second.entity));
				if(currentDomain!=newDomain)
				{
					if(currentDomain!=DOMAIN_UNDEF)
						wr->writeEndElement(); // domain name
					currentDomain=newDomain;
					wr->writeStartElement(XmlHelper::domain2DomainText(currentDomain));
				}
				switch(it->second.rtype)
				{
				case REQU_READ:
					db.select(it->second, wr, depRequs);
					break;
				case REQU_WRITE:
					db.update(wr, it->second);
					// TODO feature - create new identity
					break;
				default: // REQU_UNDEF, REQU_ACTION
					// TODO feature - implement action request
					throw new AppException(FCT ": unknown request Type", true);
				}
			}
			if(currentDomain!=DOMAIN_UNDEF)
				wr->writeEndElement(); // domain name
			if(followOrder)
				delete currentRequ;
			++followOrder;
			currentRequ=depRequs;
			depRequs=new RequestDescriptor;
		}
		delete depRequs;
		XmlHelper::finishXmlDoc(wr);
		dataBuf->close();
		emit dataReadySig(res);
		return 0; // we serve the request always synchronously
	}
	else
	{
		testResult=false;
		wr->writeStartElement(QString("metaConnAttrs"));
		try {testResult=db.selectPragma(wr, &versMaj, &versMin, &versBld);}
		catch(AppException *ex)
		{
			if(ex->getCauseCode()!=EXC_LOCALCONFAILED)
				throw ex;
			ctrc(Trc::WORK,APP,FCT)<<"test failed due to DB exception: "<<ex->getMessage()->c_str()<<endl;
			testResult=false;
		}
		wr->writeEndElement(); // metaConnAttrs
		XmlHelper::finishXmlDoc(wr);
		dataBuf->close();
	}
	return res;
}
